#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h> // 多路复用 I/O

#include <arpa/inet.h>

#include <netinet/in.h>
// #include <netinet/ip.h>

#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>

// #include <glib.h>          // GList

#include <math.h>

#include <signal.h>

int main(int argc, char *argv[])
{
    // 创建监听套接字

    int svrFd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    if (svrFd == -1)
    {
        perror("socket() failed");
        exit(EXIT_FAILURE);
    }

    // 绑定地址和端口
    struct sockaddr_in svraddr;

    memset(&svraddr, 0, sizeof(svraddr));

    svraddr.sin_family = AF_INET;
    svraddr.sin_addr.s_addr = INADDR_ANY;
    svraddr.sin_port = htons(8080);

    // bind，成功返回0，出错返回-1
    if (bind(svrFd, (struct sockaddr *)&svraddr, sizeof(struct sockaddr)) == -1)
    {
        perror("bind error");
        exit(1);
    }

    // 监听连接请求
    if (listen(svrFd, 20) == -1)
    {
        perror("listen error");
        exit(1);
    }

    // 初始化文件描述符集合
    fd_set cliFds;          // int_64[]
    FD_ZERO(&cliFds);       // 清空, 全置0
    FD_SET(svrFd, &cliFds); // 添加socket描述符, 置1

    int max_fd = svrFd; //

    printf("Server started at port %d\n", ntohs(svraddr.sin_port));

    // 解决无效遍历已关闭的的连接的问题 by 2023.1.31
    // std::vector<int> avaFds;

    while (1)
    {
        // 复制文件描述符集合，因为 select 会改变原始的集合
        fd_set tmp_fds = cliFds;

        // 调用 select 函数等待读事件发生, 检测集合中所有的 socket有没有改变
        int nready = select(max_fd + 1, &tmp_fds, NULL, NULL, NULL);

        // printf("select ok\n");

        if (nready == -1)
        {
            perror("select() failed");
            exit(EXIT_FAILURE);
        }

        // 检测到 svrFd 变化, 接收 cli sockfd
        if (FD_ISSET(svrFd, &tmp_fds))
        {
            // 获取客户端套接字
            int cli_fd = accept(svrFd, NULL, NULL);

            if (cli_fd == -1)
            {
                // perror("accept() failed");
                // exit(EXIT_FAILURE);
            }

            // 添加到描述符数组中
            FD_SET(cli_fd, &cliFds);

            // select 监听, 从 0 到最大的一位 fd,   最近的一次客户端连接就是 最大值
            max_fd = cli_fd > max_fd ? cli_fd : max_fd;
        }

        // 处理事件
        for (int i = svrFd + 1; i <= max_fd; ++i)
        {
            int sockfd = i;
            // printf("ForEach : %d->%d\n",  max_fd, sockfd);

            // 从可用fds中, 检测每一个有变化的 sockfd
            if (FD_ISSET(sockfd, &tmp_fds))
            {
                char rBuff[128] = {0};
                char wBuff[128] = {0};

                int rSize = recv(sockfd, &rBuff, sizeof(rBuff), 0);

                // 获取数据之后, 看选择性删除该套接字
                if (rSize <= 0)
                {
                    // perror("read() failed");
                    // exit(EXIT_FAILURE);
                    printf("Connection closed from socket %d\n", sockfd);
                    close(sockfd);
                    FD_CLR(sockfd, &cliFds); // 删除套接字
                    continue;
                }
                else if (rSize > 0)
                {
                    printf("Read:%s\n", rBuff);
                }
                strcpy(wBuff, "pong");
                // 发送数据给客户端
                // write(cliFd, buff, sizeof(buff));
                int wSize = send(sockfd, wBuff, sizeof(wBuff), 0);
                // wSize返回值同 rSize
            }
        }
    }

    return 0;
}
